﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Configuration;
using System.Text;
using System.Threading.Tasks;

namespace Anagramme.Model
{
    /// <summary>
    /// Classe de base pour les modèles
    /// </summary>
    /// <typeparam name="T">Type du client du service</typeparam>
    public abstract class ModelBase<T> : IService, IDisposable
        where T : class
    {
        #region Static Methods
        private static string GetConfiguration()
        {
            ClientSection clientSection = ConfigurationManager.GetSection("system.serviceModel/client") as ClientSection;
            ChannelEndpointElementCollection endpointCollection = clientSection.ElementInformation.Properties[string.Empty].Value as ChannelEndpointElementCollection;

            List<ChannelEndpointElement> endpoints = new List<ChannelEndpointElement>();
            foreach (ChannelEndpointElement endpointElement in endpointCollection)
            {
                endpoints.Add(endpointElement);
            }

            ChannelEndpointElement configuration = endpoints.Single(e => e.Contract == typeof(T).FullName);

            return configuration.Name;
        }
        #endregion

        #region Constructors
        /// <summary>
        /// Crée une nouvelle instance de la classe <see cref="ModelBase{T}"/>
        /// </summary>
        protected ModelBase()
        { 
        
        }

        /// <summary>
        /// Desctructeur de la classe <see cref="ModelBase{T}"/>
        /// </summary>
        ~ModelBase()
        {
            Dispose(false);
        }
        #endregion

        #region Fields
        private DuplexChannelFactory<T> _factory;
        #endregion

        #region Properties
        /// <summary>
        /// Client du service
        /// </summary>
        protected T Service { get; private set; }
        #endregion

        #region IService Implementation
        /// <summary>
        /// Connexion au serveur
        /// </summary>
        public void Connect()
        {
            string configurationName = GetConfiguration();
            this._factory = new DuplexChannelFactory<T>(new InstanceContext(this), configurationName);
            this.Service = this._factory.CreateChannel();
            ((ICommunicationObject)this.Service).Open();
        }

        /// <summary>
        /// Connexion au serveur
        /// </summary>
        /// <param name="address">Adresse du serveur</param>
        public void Connect(string address)
        {
            string configurationName = GetConfiguration();
            this._factory = new DuplexChannelFactory<T>(new InstanceContext(this), configurationName);
            this.Service = this._factory.CreateChannel(new EndpointAddress(address));
            ((ICommunicationObject)this.Service).Open();
        }

        /// <summary>
        /// Deconnexion du serveur
        /// </summary>
        public void Disconnect()
        {
            ((ICommunicationObject)this.Service).Close();
            this._factory.Close();
        }
        #endregion

        #region IDisposable Implementation
        /// <summary>
        /// Exécute les tâches définies par l'application associées à la libération ou à la redéfinition des ressources non managées.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Exécute les tâches définies par l'application associées à la libération ou à la redéfinition des ressources non managées.
        /// </summary>
        /// <param name="disposing">True si les ressources managées doivent être libérées, sinon False.</param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (this.Service != null)
                {
                    ((ICommunicationObject)this.Service).Close();
                    this._factory.Close();
                    this.Service = null;
                }
            }
        }
        #endregion
    }
}
